package com.heima.wemedia.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.heima.aliyun.GreenImageScan;
import com.heima.aliyun.GreenTextScan;
import com.heima.api.ApArticleSaveFeign;
import com.heima.api.TaskFeign;
import com.heima.article.dotos.ApArticleDto;
import com.heima.article.dotos.ApArticleSaveDto;
import com.heima.constants.WmNewsConstants;
import com.heima.file.service.FileStorageService;
import com.heima.schedule.dtos.Task;
import com.heima.tess4j.Tess4jClient;
import com.heima.user.model.common.dtos.ResponseResult;
import com.heima.user.model.common.enums.AppHttpCodeEnum;
import com.heima.user.utils.common.SensitiveWordUtil;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmSensitiveMapper;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.mapper.WmchannelMapper;
import com.heima.wemedia.pojos.WmChannel;
import com.heima.wemedia.pojos.WmNews;
import com.heima.wemedia.pojos.WmSensitive;
import com.heima.wemedia.pojos.WmUser;
import com.heima.wemedia.service.WmNewsScanService;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.SendResult;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmNewsScanServiceImpl implements WmNewsScanService {
    @Autowired
    private GreenTextScan greenTextScan;

    @Autowired
    private GreenImageScan greenImageScan;

    @Autowired
    private FileStorageService fileStorageService;

    @Autowired
    private WmNewsMapper wmNewsMapper;

    @Autowired
    private WmchannelMapper wmchannelMapper;

    @Autowired
    private WmUserMapper wmUserMapper;

    @Autowired
    private ApArticleSaveFeign apArticleSaveFeign;

    @Autowired
    private WmSensitiveMapper wmSensitiveMapper;

    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;

    @Autowired
    private Tess4jClient tess4jClient;

    @Autowired
    private TaskFeign taskFeign;

    private static final String TEXT = "文本";

    private static final String IMAGE = "图片";

    @Override
    @Async //异步执行
    public void submit(WmNews wmNews) {
        try {
            log.info("自媒体文章审核开始");
            // 1.获取文本图片
            Map<String, Object> ScanResult = getTextandImage(wmNews);

            //2.审核敏感词和文章图片和文本
            getSacn(wmNews, ScanResult);

        } catch (Exception e) {
            log.error("文章审核异常", e);
            wmNews.setStatus(WmNews.Status.ADMIN_AUTH.getCode());
            //保存审核结果
            wmNewsMapper.updateById(wmNews);
        }
        if (wmNews.getPublishTime() == null){
            //回填本章article_id
            saveAppArticle(wmNews);
        }else {
            Task task = new Task();
            task.setExecuteTime(wmNews.getPublishTime());
            task.setTaskType(30);
            task.setPriority(5);
            task.setParameters(JSON.toJSONString(wmNews));
            taskFeign.addTask(task);
            log.info("定时任务添加成功");
        }

        //异步消息发送
        ListenableFuture<SendResult<String, String>> future = kafkaTemplate.send(WmNewsConstants.WM_NEWS_AUTO_TOPIC, JSONArray.toJSONString(wmNews));

        //异步回调
        future.addCallback(new ListenableFutureCallback<SendResult<String, String>>() {
           @Override
           public void onFailure(Throwable throwable) {
               log.error("异步消息失败:{}", throwable.getMessage());
           }
           @Override
           public void onSuccess(SendResult<String, String> stringStringSendResult) {
               log.info("异步消息成功:{}", stringStringSendResult);

           }
       });

    }


    /**
     * 保存app文章
     *
     * @param wmNews
     * @return
     */
    @Override
    public void saveAppArticle(WmNews wmNews) {
        ApArticleSaveDto dto = new ApArticleSaveDto();
        BeanUtils.copyProperties(wmNews, dto);

        WmUser wmUser = wmUserMapper.selectById(wmNews.getUserId());
        dto.setAuthorName(wmUser == null ? null : wmUser.getName());

        WmChannel wmchannel = wmchannelMapper.selectById(wmNews.getChannelId());
        dto.setChannelName(wmchannel == null ? null : wmchannel.getName());

        dto.setAuthorId(wmNews.getUserId());
        dto.setLayout(wmNews.getType());
        dto.setPublishTime(LocalDateTime.now());


        ResponseResult responseResult = apArticleSaveFeign.save(dto);
        log.info("保存app文章结果:{}", responseResult);
        if (responseResult.getCode() == AppHttpCodeEnum.SUCCESS.getCode()) {
            //更新状态和文章id
            wmNews.setStatus(WmNews.Status.PUBLISHED.getCode());
            wmNews.setArticleId((Long) responseResult.getData());
            wmNewsMapper.updateById(wmNews);
        }

    }


    /**
     * 文章审核
     *
     * @param scanResult
     * @param wmNews
     */
    private void getSacn(WmNews wmNews, Map scanResult) throws Exception {
        //获取文本
        Map textMap = greenTextScan.greeTextScan(scanResult.get("text")+"");
        StringBuilder text = new StringBuilder(scanResult.get("text").toString());

        //获取图片
        Set<String> imges = (Set) scanResult.get("images");
        List<byte[]> images = imges.stream().map(img -> {
            byte[] bytes = fileStorageService.downLoadFile(img);
            return bytes;
        }).collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(images)) {
            for (byte[] image : images) {
                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(image);
                try {
                    BufferedImage bufferedImage = ImageIO.read(byteArrayInputStream);
                    String text1 = tess4jClient.doOCR(bufferedImage);
                    text.append("-").append(text1);
                } catch (IOException e) {
                    log.error("图片转文字异常", e);
                }
            }
        }

        //获取敏感词
        getSensitiveWord(String.valueOf(text),wmNews);
        if (wmNews.getStatus() == WmNews.Status.FAIL.getCode() ){
            log.info("敏感词审核拒绝,不走文本图片审核");
            return;
        }

        gettypeSacn(textMap, TEXT, wmNews);
        //如果文本审核拒绝或者文本审核需要人工审核就不走图片审核
        if (wmNews.getStatus() == WmNews.Status.FAIL.getCode() || wmNews.getStatus() == WmNews.Status.ADMIN_AUTH.getCode()) {
            log.info("文本审核拒绝或者文本审核需要人工审核,不走图片审核");
            return;
        }

        Map ImageMap = greenImageScan.imageScan(images);

        gettypeSacn(ImageMap, IMAGE, wmNews);

        if (wmNews.getStatus() == WmNews.Status.FAIL.getCode() || wmNews.getStatus() == WmNews.Status.ADMIN_AUTH.getCode()) {
            log.info("图片审核拒绝或者图片审核需要人工审核,不走文章保存");
            return;
        }
        wmNewsMapper.updateById(wmNews);
    }

    /**
     * 敏感词审核
     * @param
     * @param wmNews
     */
    private void getSensitiveWord( String text, WmNews wmNews) {
        //自定义敏感词
        //获取自定义敏感词
        List<WmSensitive> wmSensitives = wmSensitiveMapper.selectList(null);
        if (CollectionUtils.isEmpty(wmSensitives)){
            log.info("自定义敏感词为空");
            return;
        }
        //封装成list
        List<String> collect = wmSensitives.stream().map(WmSensitive::getSensitives).collect(Collectors.toList());
        //存入字典库
        SensitiveWordUtil.initMap(collect);
        //匹配关键词命中次数
        Map<String, Integer> matchWords = SensitiveWordUtil.matchWords(text);
        if (matchWords.size() > 0) {
            log.info("文本存在敏感词:" + matchWords);
            wmNews.setStatus(WmNews.Status.FAIL.getCode());
            wmNews.setReason("文本存在敏感词" + matchWords);
            wmNewsMapper.updateById(wmNews);
            return;
        }
    }

    /**
     * 文章审核
     *
     * @param map
     * @param type
     * @param wmNews
     */
    private void gettypeSacn(Map map, String type, WmNews wmNews) {
        if (map == null) {
            log.info("文本文章为空");
            return;
        }
        if (map.get("suggestion").equals("block")) {
            wmNews.setStatus(WmNews.Status.FAIL.getCode());
            wmNews.setReason(type + "审核不通过" + map.get("label"));
        } else if (map.get("suggestion").equals("review")) {
            wmNews.setStatus(WmNews.Status.ADMIN_AUTH.getCode());
            wmNews.setReason(type + "审核需要人工审核" + map.get("label"));
        } else if (map.get("suggestion").equals("pass")) {
            wmNews.setStatus(WmNews.Status.SUBMIT.getCode());
            wmNews.setReason(type + "审核通过");
        }

        log.info("文章审核结果:{}", wmNews);
    }


    /**
     * 获取文本和图片
     *
     * @param wmNews
     * @return
     */
    private Map<String, Object> getTextandImage(WmNews wmNews) {
        String content = wmNews.getContent();

        Map<String, Object> textScanResult = new HashMap<>();

        //图片
        Set<String> images = new HashSet<>();
        //文本
        StringBuilder textScan = new StringBuilder();

        List<Map> mapList = JSONArray.parseArray(content, Map.class);
        for (Map map : mapList) {
            String type = map.get("type").toString();
            String value = map.get("value").toString();
            if ("image".equals(type)) {
                images.add(value);
            }
            if ("text".equals(type)) {
                textScan.append(value);
            }
        }

        //获取封面图片
        if (wmNews.getImages() != null) {
            images.addAll(Arrays.asList(wmNews.getImages().split(",")));
        }

        String title = wmNews.getTitle();
        textScan.append("--").append(title);
        textScanResult.put("text", textScan.toString());
        textScanResult.put("images", images);

        return textScanResult;
    }
}
